package com.audi.box.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import lib.relay.DevListInfo;
import lib.relay.RegistRelayFunc.SignUpStatus;
import lib.relay.RelayInterface;
import lib.relay.onAuthListener;

import android.content.Context;
import android.util.Log;

public class Server implements onAuthListener {
	private Context context;
	private MessageHandler messageHandler;
	private int port;
	private ServerSocket serverSocket;
	private Thread serverThread;
	protected ServerListenerCallBack listener;
	
	private String id;
	private String pw;

	public Server(Context context, int port, String id, String pw,
			ServerListenerCallBack serverListenerCB) {
		this.context = context;
		this.port = port;
		this.listener = serverListenerCB;
		this.id = id;
		this.pw = pw;
		initiate();
	}

	public void initiate() {
		try {
			messageHandler = new MessageHandler(context);
			new RelayInterface();
			RelayInterface.SetInterface(this.context);
			RelayInterface.m_onAuthListener = this;
		} catch (Exception e) {
			Log.d("AudiBox", "[Server.java] Initlize fail", e);
			listener.startCallback(false);
		}
	}

	/**
	 * Login by Asus id
	 * 
	 * @param id
	 * @param pw
	 */
	public void running() {
		Log.d("AudiBox", "[Server.java] Login id=" + id + " ,pw=" + pw);
		RelayInterface.GetRelaySAuth(id, pw);
	}

	/**
	 * Logout Asus id
	 */
	public void stop() {
		if (serverSocket != null) {
			try {
				serverSocket.close();
				this.serverThread.interrupt();
			} catch (IOException e) {
			}
		}
		logout();
	}

	public void logout() {
		RelayInterface.RelayDeInit();
	}

	/**
	 * Run server listerning socket
	 */
	private void start() {
		this.serverThread = new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					serverSocket = new ServerSocket();
					serverSocket.setReuseAddress(true);
					serverSocket.bind(new InetSocketAddress(port));
					Log.d("AudiBox",
							"[Server.java] Server waiting ... on port " + port);
					while (true) {
						Socket connected = serverSocket.accept();
						new ClientThread(connected).start();
						Log.d("AudiBox", "[Server.java] Client connected ");
					}
				} catch (Exception e) {
					Log.e("AudiBox",
							"[Server.java] Server error: " + e.toString());
				}

			}
		});
		this.serverThread.start();
		this.listener.startCallback(true);
	}

	class ClientThread extends Thread {
		private Socket myClientSocket = null;

		public ClientThread(Socket clientSocket) {
			this.myClientSocket = clientSocket;
		}

		/**
		 * Process socket
		 */
		public void run() {
			try {
				InputStream is = myClientSocket.getInputStream();
				OutputStream out = myClientSocket.getOutputStream();

				if ((is != null) && (out != null)) {
					int iLen = 1024;
					byte[] unReceive = new byte[iLen];
					for (int i = 0; i < iLen; ++i)
						unReceive[i] = 0;

					byte[] data = getBytesFromInputStream(is);

					String strReceive = new String(data);
					Log.d("AudiBox ", "[Server.java] Receive Tcp: "
							+ strReceive);
					listener.processCallback("Receive:", "Msg: strReceive");					
					String strSend = messageHandler.processMessage(strReceive);
					Log.d("AudiBox", "[Server.java] Send Tcp: " + strSend);
					listener.processCallback("Send:", "Msg: strReceive");

					byte[] dataSend = strSend.getBytes();
					int size = dataSend.length;
					byte hi = (byte) (size / 255);
					byte lo = (byte) (size % 255);
					out.write(hi);
					out.write(lo);
					out.write(dataSend);
					out.flush();
				}

				if (myClientSocket != null) {
					myClientSocket.close();
					myClientSocket = null;
				}
			} catch (IOException e) {
				Log.d("AudiBox", "[Server.java] Process socket fail.", e);
			}
		}

		/**
		 * Get all byte in InputSteam
		 * 
		 * @param is
		 * @return
		 * @throws IOException
		 */
		public byte[] getBytesFromInputStream(InputStream is)
				throws IOException {

			byte hi = (byte) is.read();
			if (hi == -1) {
				throw new IOException("End of stream");
			}

			byte lo = (byte) is.read();
			if (lo == -1) {
				throw new IOException("End of stream");
			}

			int size = (hi & 0xFF) * 255 + (lo & 0xFF);
			Log.d("AudiBox", "[Server.java] Recieve Tcp size: " + size);
			byte[] data = new byte[size];
			int read = 0;
			while (read != size) {
				int n = is.read(data, read, size - read);
				if (n == -1) {
					throw new IOException("End of stream");
				}
				read += n;
			}

			return data;
		}

	}

	/**
	 * Login callback
	 */
	@Override
	public void GetAuthFinished(boolean bValue) {
		if (bValue == true) {
			Log.d("AudiBox", "[Server.java] Login Relay successfully.");

			int iPortNum = 8;
			String[][] port = new String[2][iPortNum];
			port[0] = new String[] { "5050", "5066", "5067", "5069", "16120",
					"16121", "16122", "7777" };
			port[1] = new String[] { "5050", "5066", "5067", "5069", "16120",
					"16121", "16122", "7777" };

			RelayInterface.RelayInit(port, iPortNum);
		} else {
			Log.d("AudiBox", "[Server.java] Failed to login Relay.");
			listener.startCallback(false);
		}

	}

	/**
	 * List replay callback
	 */
	@Override
	public void ListProfileFinished(List<DevListInfo> relay_list) {
		Log.d("AudiBox", "[Server.java] ListProfileFinished.");
	}

	/**
	 * Logout callback
	 */
	@Override
	public void LogoutFinished(boolean bValue) {
		if (bValue == true) {
			Log.d("AudiBox", "[Server.java] Logout Relay successfully.");
			listener.stopCallback(true);
		} else {
			Log.d("AudiBox", "[Server.java] Failed to Logout Relay.");
			listener.stopCallback(false);
		}
	}

	/**
	 * Initialize relay callback
	 */
	@Override
	public void CBInit(boolean bValue) {
		if (bValue == true) {
			Log.d("AudiBox", "[Server.java] Initialize Relay successfully.");
			start();
		} else {
			Log.d("AudiBox", "[Server.java] Failed to Initialize Relay.");
			listener.startCallback(false);
		}

	}

	/**
	 * Makecall callback
	 */
	@Override
	public void CBMakeCall(boolean bValue) {
		Log.d("AudiBox", "[Server.java] CBMakeCall.");
	}

	/**
	 * HangUp callback
	 */
	@Override
	public void CBHangUp(boolean bValue) {
		Log.d("AudiBox", "[Server.java] CBHangUp.");
	}

	/**
	 * Deinit relay callback
	 */
	@Override
	public void CBDeInit(boolean bValue) {
		if (bValue == true) {
			Log.d("AudiBox", "[Server.java] DeInit Relay successfully.");
			RelayInterface.LogoutRelayS();
		} else {
			Log.d("AudiBox", "[Server.java] Failed to DeInit Relay.");
			listener.stopCallback(false);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see lib.relay.onAuthListener#CBClientSocket(java.lang.String)
	 */
	@Override
	public void CBClientSocket(String CBString) {
		Log.d("AudiBox", "[Server.java] CBClientSocket.");

	}

	/* (non-Javadoc)
	 * @see lib.relay.onAuthListener#GetSignUp(lib.relay.RegistRelayFunc.SignUpStatus)
	 */
	@Override
	public void GetSignUp(SignUpStatus mSignUpStatus) {
		// TODO Auto-generated method stub
		
	}
}
